package service

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/wonderivan/logger"
	"io"
	"k8s-platform/config"
	"k8s-platform/db"
	"k8s-platform/model"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var JenkinsGet jenkinsget

type jenkinsget struct{}

type StageStatus struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Status    string `json:"status"`
	StartTime int64  `json:"startTimeMillis"`
	Duration  int64  `json:"durationMillis"`
}

type Response struct {
	Stages []StageStatus `json:"stages"`
	ID     string        `json:"id"`
	Status string        `json:"status"`
}

type JobInfo struct {
	Name         string
	Status       string
	TriggerMode  string
	Engineering  string
	SourceBranch string
	BuildId      int64
	Test         []BuildsInfo
}

type BuildsInfo struct {
	BuildID   string
	Name      string
	Status    string
	StartTime time.Time
	Duration  string
}

func (j *jenkinsget) GetViewNames() ([]string, error) {
	ctx := context.Background()
	views, err := Jenkins_init.JenkinsClient.GetAllViews(ctx)
	if err != nil {
		log.Println("jenkins获取view列表失败", err)
		return nil, errors.New("jenkins获取view列表失败" + err.Error())
	}
	var viewNames []string
	for _, v := range views {
		viewNames = append(viewNames, v.GetName())
	}
	return viewNames, nil
}

func (j *jenkinsget) GetJobs(FilterName string, ViewName string) (joblist []JobInfo, err error) {

	jenkinsConfigTest := &config.JenkinsConfigTest{}
	mysqlconfig, err := jenkinsConfigTest.ConfigTest()
	if err != nil {
		log.Fatalf("Failed to parse configuration: %v", err)
	}

	ctx := context.Background()
	if ViewName != "" {
		jobs, err := Jenkins_init.JenkinsClient.GetView(ctx, ViewName)
		if err != nil {
			log.Println("jenkins获取job列表失败", err)
			return nil, errors.New("jenkins获取job列表失败" + err.Error())
		}
		for _, job := range jobs.GetJobs() {
			// 获取最新一次流水线任务列表（非历史列表数据）
			job, err := Jenkins_init.JenkinsClient.GetJob(ctx, job.Name)
			if err != nil {
				logger.Error("查看详情失败", err)
				return nil, errors.New("查看详情失败" + err.Error())
			}

			if FilterName != "" {
				fmt.Printf("使用过滤")
				//精确查询
				//if job.GetName() != FilterName {
				//	continue
				//}
				//模糊查询
				if !strings.Contains(job.GetName(), FilterName) {
					continue
				}
			}

			// 获取上一次构建
			lastBuild, err := job.GetLastBuild(ctx)
			var status string
			// 获取上一次构建号
			var buildiddata int64
			var body []byte
			if lastBuild != nil {
				// 获取上一次构建的状态和结果
				status = lastBuild.GetResult()
				// 获取上一次构建号
				jobid, err := Jenkins_init.JenkinsClient.GetJob(ctx, job.GetName()) // Replace with your job name
				buildIDs, err := jobid.GetAllBuildIds(ctx)
				buildiddata = buildIDs[0].Number
				if err != nil {
					log.Fatalf("Failed to get job: %v", err)
				}
			} else {
				status = "InitError"
				//continue
			}
			//执行方式：手动构建，提交构建，定时构建
			triggerMode := "手动触发"

			// 获取任务配置文件
			config2, err := job.GetConfig(ctx)
			if err != nil {
				fmt.Println(err)
			}
			config := strings.ReplaceAll(config2, "&apos;", "")

			//从数据库中获取git数据
			//data := &model.Git{}
			//username := "git"
			//result := db.DB.Where("name = ?", username).First(&data)
			//if result.Error != nil {
			//	logger.Error("获取git库注册数据不存在,请注册git库")
			//	return nil, errors.New("获取git库注册数据不存在，请注册git库")
			//}

			//正则切割git库名称
			var engineering string // Declare 'engineering' outside the 'if' block
			Engineering := regexp.MustCompile(`git clone\s+http://.*\/([^/]+)\.git`)
			//Engineering := regexp.MustCompile(gitlab_url + `/(.*)\.git`)
			if Engineering != nil {
				matches := Engineering.FindStringSubmatch(config)
				if len(matches) > 0 {
					engineering = matches[1] // Assign value to 'engineering' within the 'if' block
				} else {
					engineering = "nil"
				}
			}

			//正则切割分支名称
			var sourceBranch string
			Branch := regexp.MustCompile(`branche = '(.*)'`)
			if Branch != nil {
				matches := Branch.FindStringSubmatch(config)
				if len(matches) > 0 {
					sourceBranch = matches[1] // Assign value to 'engineering' within the 'if' block
				} else {
					sourceBranch = "nil"
				}
			}

			//添加构建数据
			buildNumberString := strconv.FormatInt(buildiddata, 10)
			url := mysqlconfig.Jenkins_url + "/job/" + job.GetName() + "/" + buildNumberString + "/wfapi/describe"
			user := mysqlconfig.Jenkins_user
			pass := mysqlconfig.Jenkins_pass
			//url := "http://47.97.202.142:44496/job/" + job.GetName() + "/" + buildNumberString + "/wfapi/describe"
			//user := "admin"
			//pass := "117fa16e4f4f853377f9e85d1b6a363129"
			req, err := http.NewRequest("GET", url, nil)
			if err != nil {
				logger.Error(job.GetName()+"连接jenkins失败", err)
				return nil, errors.New(job.GetName() + "连接jenkins失败" + err.Error())
			}
			// Add authentication headers if required
			req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(user+":"+pass)))

			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				logger.Error(job.GetName()+"连接jenkins格式化失败", err)
				return nil, errors.New(job.GetName() + "连接jenkins格式化失败" + err.Error())
			}

			//var body []byte
			//获取jenkinsapi为404时，补充参数
			if resp.StatusCode != http.StatusOK {
				s := `{"stages":[{"id":"nil","name":"nil","status":"InitError","startTimeMillis":1617896800000,"durationMillis":10000}],"id":"nil","status":"InitError"}`
				body = []byte(s)
			} else {
				var err error
				body, err = io.ReadAll(resp.Body)
				if err != nil {
					logger.Error(job.GetName()+"转义body失败", err)
					return nil, errors.New(job.GetName() + "转义body失败" + err.Error())
				}
			}

			defer resp.Body.Close()

			// Declare a variable of the type Response to hold the parsed data
			var response Response

			// Unmarshal the JSON data into the response variable
			Unmarshalerr := json.Unmarshal(body, &response)
			if Unmarshalerr != nil {
				logger.Error(job.GetName()+"转义json失败", err)
				return nil, errors.New(job.GetName() + "转义json失败" + err.Error())
			}

			var buildslist []BuildsInfo
			for _, Buildsjob := range response.Stages {
				StartTime := time.UnixMilli(Buildsjob.StartTime)
				Duration := time.Duration(Buildsjob.Duration) * time.Second / 1000
				Durationms := Duration.String()
				buildslist = append(buildslist, BuildsInfo{BuildID: Buildsjob.ID, Name: Buildsjob.Name, Status: Buildsjob.Status, StartTime: StartTime, Duration: Durationms})
			}

			joblist = append(joblist, JobInfo{Name: job.GetName(), BuildId: buildiddata, Status: status, TriggerMode: triggerMode, Engineering: engineering, SourceBranch: sourceBranch, Test: buildslist})

		}
	} else {
		jobs, err := Jenkins_init.JenkinsClient.GetAllJobs(ctx)
		if err != nil {
			log.Println("jenkins获取job列表失败", err)
			return nil, errors.New("jenkins获取job列表失败" + err.Error())
		}
		for _, job := range jobs {
			// 获取最新一次流水线任务列表（非历史列表数据）
			job, err := Jenkins_init.JenkinsClient.GetJob(ctx, job.GetName())
			if err != nil {
				logger.Error("查看详情失败", err)
				return nil, errors.New("查看详情失败" + err.Error())
			}

			if FilterName != "" {
				fmt.Printf("使用过滤")
				//精确查询
				//if job.GetName() != FilterName {
				//	continue
				//}
				//模糊查询
				if !strings.Contains(job.GetName(), FilterName) {
					continue
				}
			}

			// 获取上一次构建
			lastBuild, err := job.GetLastBuild(ctx)
			var status string
			// 获取上一次构建号
			var buildiddata int64
			var body []byte
			if lastBuild != nil {
				// 获取上一次构建的状态和结果
				status = lastBuild.GetResult()
				// 获取上一次构建号
				jobid, err := Jenkins_init.JenkinsClient.GetJob(ctx, job.GetName()) // Replace with your job name
				buildIDs, err := jobid.GetAllBuildIds(ctx)
				buildiddata = buildIDs[0].Number
				if err != nil {
					log.Fatalf("Failed to get job: %v", err)
				}
			} else {
				status = "InitError"
				//continue
			}
			//执行方式：手动构建，提交构建，定时构建
			triggerMode := "手动触发"

			// 获取任务配置文件
			config2, err := job.GetConfig(ctx)
			if err != nil {
				fmt.Println(err)
			}

			config := strings.ReplaceAll(config2, "&apos;", "")

			//从数据库中获取git数据
			//data := &model.Git{}
			//username := "git"
			//result := db.DB.Where("name = ?", username).First(&data)
			//if result.Error != nil {
			//	logger.Error("获取git库注册数据不存在,请注册git库")
			//	return nil, errors.New("获取git库注册数据不存在，请注册git库")
			//}

			//正则切割git库名称
			var engineering string // Declare 'engineering' outside the 'if' block
			Engineering := regexp.MustCompile(`git clone\s+http://.*\/([^/]+)\.git`)
			//Engineering := regexp.MustCompile(gitlab_url + `/(.*)\.git`)
			if Engineering != nil {
				matches := Engineering.FindStringSubmatch(config)
				if len(matches) > 0 {
					engineering = matches[1] // Assign value to 'engineering' within the 'if' block
				} else {
					engineering = "nil"
				}
			}

			//正则切割分支名称
			var sourceBranch string
			Branch := regexp.MustCompile(`branche = (.*)`)
			if Branch != nil {
				matches := Branch.FindStringSubmatch(config)
				if len(matches) > 0 {
					sourceBranch = matches[1] // Assign value to 'engineering' within the 'if' block
				} else {
					sourceBranch = "nil"
				}
			}

			//添加构建数据
			buildNumberString := strconv.FormatInt(buildiddata, 10)
			url := mysqlconfig.Jenkins_url + "/job/" + job.GetName() + "/" + buildNumberString + "/wfapi/describe"
			user := mysqlconfig.Jenkins_user
			pass := mysqlconfig.Jenkins_pass
			//url := "http://47.97.202.142:44496/job/" + job.GetName() + "/" + buildNumberString + "/wfapi/describe"
			//user := "admin"
			//pass := "117fa16e4f4f853377f9e85d1b6a363129"
			req, err := http.NewRequest("GET", url, nil)
			if err != nil {
				logger.Error(job.GetName()+"连接jenkins失败", err)
				return nil, errors.New(job.GetName() + "连接jenkins失败" + err.Error())
			}
			// Add authentication headers if required
			req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(user+":"+pass)))

			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				logger.Error(job.GetName()+"连接jenkins格式化失败", err)
				return nil, errors.New(job.GetName() + "连接jenkins格式化失败" + err.Error())
			}

			//var body []byte
			//获取jenkinsapi为404时，补充参数
			if resp.StatusCode != http.StatusOK {
				s := `{"stages":[{"id":"nil","name":"nil","status":"InitError","startTimeMillis":1617896800000,"durationMillis":10000}],"id":"nil","status":"InitError"}`
				body = []byte(s)
			} else {
				var err error
				body, err = io.ReadAll(resp.Body)
				if err != nil {
					logger.Error(job.GetName()+"转义body失败", err)
					return nil, errors.New(job.GetName() + "转义body失败" + err.Error())
				}
			}

			defer resp.Body.Close()

			// Declare a variable of the type Response to hold the parsed data
			var response Response

			// Unmarshal the JSON data into the response variable
			Unmarshalerr := json.Unmarshal(body, &response)
			if Unmarshalerr != nil {
				logger.Error(job.GetName()+"转义json失败", err)
				return nil, errors.New(job.GetName() + "转义json失败" + err.Error())
			}

			var buildslist []BuildsInfo
			for _, Buildsjob := range response.Stages {
				StartTime := time.UnixMilli(Buildsjob.StartTime)
				Duration := time.Duration(Buildsjob.Duration) * time.Second / 1000
				Durationms := Duration.String()
				buildslist = append(buildslist, BuildsInfo{BuildID: Buildsjob.ID, Name: Buildsjob.Name, Status: Buildsjob.Status, StartTime: StartTime, Duration: Durationms})
			}

			joblist = append(joblist, JobInfo{Name: job.GetName(), BuildId: buildiddata, Status: status, TriggerMode: triggerMode, Engineering: engineering, SourceBranch: sourceBranch, Test: buildslist})

		}
	}

	return joblist, nil
}

func (j *jenkinsget) RunJob(jenkinsjoblist *model.Jenkinsjoblist) (data string, err error) {
	ctx := context.Background()
	// Replace "#jobname" with the actual job name in your Jenkins instance
	_, err = Jenkins_init.JenkinsClient.BuildJob(ctx, jenkinsjoblist.JobName, nil)
	if err != nil {
		logger.Error(jenkinsjoblist.JobName+"执行流水线失败", err)
		return jenkinsjoblist.JobName, errors.New(jenkinsjoblist.JobName + "执行流水线失败" + err.Error())
	}

	jobrun := jenkinsjoblist.JobID + 1
	jenkinsjoblist.JobID = jobrun
	tx := db.DB.Create(&jenkinsjoblist)
	if tx.Error != nil {
		logger.Error(fmt.Sprintf("添加credential失败，%v\n", tx.Error))
		return "nil", errors.New(fmt.Sprintf("添加credential失败，%v\n", tx.Error))
	}

	piplelinedata, err := Jenkinsjob.GetConfig(jenkinsjoblist.JobName)
	config := db.DB.First(&jenkinsjoblist, "job_name = ? AND job_id = ?", jenkinsjoblist.JobName, jobrun).Update("piplelinedata", piplelinedata)
	if config.Error != nil {
		log.Printf("Failed to find Jenkinsjoblist entry: %v\n", config.Error)
		return
	}

	//正则切割git库名称
	var engineering string // Declare 'engineering' outside the 'if' block
	Engineering := regexp.MustCompile(`git clone\s+http://.*\/([^/]+)\.git`)
	if Engineering != nil {
		matches := Engineering.FindStringSubmatch(piplelinedata)
		if len(matches) > 0 {
			engineering = matches[1] // Assign value to 'engineering' within the 'if' block
		} else {
			engineering = "nil"
		}
	}

	//正则切割分支名称
	var sourceBranch string

	Branch := regexp.MustCompile(`branche = '(.*?)'`)
	if Branch != nil {
		matches := Branch.FindStringSubmatch(piplelinedata)
		if len(matches) > 0 {
			sourceBranch = matches[1] // Assign value to 'engineering' within the 'if' block
		} else {
			sourceBranch = "nil"
		}
	}

	engineering_sourceBranch := engineering + "/" + sourceBranch

	gitlist_name := db.DB.First(&jenkinsjoblist, "job_name = ? AND job_id = ?", jenkinsjoblist.JobName, jobrun).Update("gitlist_name", engineering_sourceBranch)
	if gitlist_name.Error != nil {
		log.Printf("Failed to find Jenkinsjoblist entry: %v\n", gitlist_name.Error)
		return
	}

	//正则切割git库名称
	var giturl string
	gitlaburl := regexp.MustCompile(`\s+http://.*\/([^/]+)\.git`)
	//Engineering := regexp.MustCompile(gitlab_url + `/(.*)\.git`)
	if gitlaburl != nil {
		matches := gitlaburl.FindStringSubmatch(piplelinedata)
		if len(matches) > 0 {
			giturl = matches[0] // Assign value to 'engineering' within the 'if' block
		} else {
			giturl = "nil"
		}
	}

	gitlisturl := db.DB.First(&jenkinsjoblist, "job_name = ? AND job_id = ?", jenkinsjoblist.JobName, jobrun).Update("gitlist_url", giturl)
	if gitlisturl.Error != nil {
		log.Printf("Failed to find Jenkinsjoblist entry: %v\n", gitlisturl.Error)
		return
	}

	return "nil", nil
}

func (j *jenkinsget) Joblist(jobname string) ([]*model.Jenkinsjoblist, error) {
	var JenkinsJobList []*model.Jenkinsjoblist
	result := db.DB.Where("job_name = ?", jobname).Find(&JenkinsJobList)

	// Check for errors
	if result.Error != nil {
		return nil, fmt.Errorf("failed to find Jenkinsjoblist entry: %v", result.Error)
	}

	return JenkinsJobList, nil
}

func (j *jenkinsget) StouJob(jobname string, jobid int64) (data string, err error) {
	ctx := context.Background()
	// Replace "#jobname" with the actual job name in your Jenkins instance
	// Replace "your-job-name" with the name of your job
	job, err := Jenkins_init.JenkinsClient.GetJob(ctx, jobname)
	if err != nil {
		panic(err)
	}

	// Replace  1 with the build number you want to stop
	build, err := job.GetBuild(ctx, jobid)
	if err != nil {
		panic(err)
	}

	// Stop the build
	_, err = build.Stop(ctx)
	if err != nil {
		logger.Error(jobname+"停止流水线失败", err)
		return jobname, errors.New(jobname + "停止流水线失败" + err.Error())
	}

	return "停止" + jobname + "流水线成功", nil
}

func (j *jenkinsget) Getnodes() (nodelist []string, err error) {
	ctx := context.Background()
	nodes, _ := Jenkins_init.JenkinsClient.GetAllNodes(ctx)

	if err != nil {
		logger.Error("jenkins获取node列表失败", err)
		return nodelist, errors.New("jenkins获取node列表失败" + err.Error())
	}

	for _, node := range nodes {
		nodeNameWithoutSpaces := strings.ReplaceAll(node.GetName(), "Built-In Node", "built-in")
		nodelist = append(nodelist, nodeNameWithoutSpaces)

	}

	return nodelist, err
}

// //实现jenkins流水线状态和平台实时同步////
type JenkinsNotification struct {
	Name        string `json:"name"`
	DisplayName string `json:"display_name"`
	URL         string `json:"url"`
	Build       Build  `json:"build"`
}

type Build struct {
	Status    string    `json:"status"`
	FullURL   string    `json:"full_url"`
	Number    int       `json:"number"`
	QueueID   int       `json:"queue_id"`
	Timestamp int64     `json:"timestamp"`
	Duration  int       `json:"duration"`
	Phase     string    `json:"phase"`
	URL       string    `json:"url"`
	SCM       SCM       `json:"scm"`
	Log       string    `json:"log"`
	Notes     string    `json:"notes"`
	Artifacts Artifacts `json:"artifacts"`
}

type SCM struct {
	Changes  []interface{} `json:"changes"`
	Culprits []interface{} `json:"culprits"`
}

type Artifacts struct {
}

// 需要jenkins安装Websocket Notifier插件
var buildStatusChan = make(chan JenkinsNotification)

func (j *jenkinsget) PipleJenkinsSatus(w http.ResponseWriter, r *http.Request) {
	body, err := io.ReadAll(r.Body)
	if err != nil {
		log.Printf("Error reading body: %v", err)
		return
	}

	var notification JenkinsNotification
	err = json.Unmarshal(body, &notification)
	if err != nil {
		log.Printf("Error parsing JSON: %v", err)
		return
	}
	//fmt.Printf("Received Jenkins webhook: %s\n", notification.Build.Status)

	if notification.Build.Status != "" {
		buildStatusChan <- notification

		job_name := notification.Name
		job_id := notification.Build.Number
		job_status := notification.Build.Status
		var jenkinsjoblist *model.Jenkinsjoblist
		result := db.DB.First(&jenkinsjoblist, "job_name = ? AND job_id = ?", job_name, job_id).Update("status", job_status)
		if result.Error != nil {
			log.Printf("Failed to find Jenkinsjoblist entry: %v\n", result.Error)
			return
		}

		timestamp := notification.Build.Timestamp
		//timestamp := int64(1709643054337)
		// Convert milliseconds to seconds
		seconds := timestamp / 1000
		nanoseconds := (timestamp % 1000) * int64(time.Millisecond)
		// Convert to time.Time
		jobtimestamp := time.Unix(seconds, nanoseconds)

		timestampdata := db.DB.First(&jenkinsjoblist, "job_name = ? AND job_id = ?", job_name, job_id).Update("timestamp", jobtimestamp)
		if timestampdata.Error != nil {
			log.Printf("Failed to find Jenkinsjoblist entry: %v\n", result.Error)
			return
		}

		jobduration := notification.Build.Duration / 1000
		jobduration_string := strconv.Itoa(jobduration) + "s"
		durationdata := db.DB.First(&jenkinsjoblist, "job_name = ? AND job_id = ?", job_name, job_id).Update("duration", jobduration_string)
		if durationdata.Error != nil {
			log.Printf("Failed to find Jenkinsjoblist entry: %v\n", result.Error)
			return
		}
	} else {
		buildStatusChan <- notification
	}
}

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		// Allow all connections.
		return true
	},
}

func (j *jenkinsget) VuePipleSatus(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("Upgrade error: %v", err)
		return
	}
	defer ws.Close()

	// Goroutine to handle incoming WebSocket messages
	go func() {
		for {
			_, msg, err := ws.ReadMessage()
			if err != nil {
				log.Printf("Read error: %v", err)
				break
			}
			fmt.Printf("Received message: %s\n", msg)
		}
	}()

	// Main loop to listen for build status updates and send them to the client
	for {
		select {
		case buildStatus := <-buildStatusChan:
			// Convert the buildStatus struct to JSON
			statusMessage, err := json.Marshal(buildStatus)
			if err != nil {
				log.Printf("Error marshaling JSON: %v", err)
				break
			}
			// Send the JSON string to the WebSocket client
			err = ws.WriteMessage(websocket.TextMessage, statusMessage)
			if err != nil {
				log.Printf("Write error: %v", err)
				break
			}
		case <-time.After(time.Second * 30): // Timeout after 30 seconds
			log.Println("Timeout waiting for build status updates")
			return
		}
	}

}
